package com.bilibili.service.impl;

import com.bilibili.dao.repository.UserInfoRepository;
import com.bilibili.dao.repository.VideoRepository;
import com.bilibili.domain.entity.UserInfo;
import com.bilibili.domain.entity.Video;
import com.bilibili.service.ElasticSearchService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * projectName:com.bilibili.service.impl
 *
 * @author 飞
 * @create by 2024/6/1816:47
 * description:
 */
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {
    @Resource
    private VideoRepository videoRepository;
    @Resource
    private UserInfoRepository userInfoRepository;
    @Resource
    private RestHighLevelClient restHighLevelClient;
    @Override
    public void addVideo(Video video) {
        videoRepository.save(video);
    }

    @Override
    public Video getVideo(String keyword) {
        return videoRepository.findByTitleLike(keyword);
    }

    @Override
    public void deleteAllVideos() {
        videoRepository.deleteAll();
    }

    @Override
    public void addUserInfo(UserInfo userInfo) {
        userInfoRepository.save(userInfo);
    }

    /**
     * 根据关键字、页码和每页大小获取搜索内容。
     *
     * @param keyword 搜索关键字。
     * @param pageNo 当前页码。
     * @param pageSize 每页大小。
     * @return 包含搜索结果的列表，每个结果是一个包含关键字匹配信息的映射。
     */
    @Override
    public List<Map<String, Object>> getContents(String keyword, Integer pageNo, Integer pageSize) throws IOException {
        // 定义搜索的索引名称
        String[] index = new String[]{"user_info","videos"};
        SearchRequest searchRequest = new SearchRequest(index);
        // 构建搜索源构建器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 设置分页信息
        searchSourceBuilder.from(pageNo-1);
        searchSourceBuilder.size(pageSize);
        // 构建多匹配查询器，用于匹配标题、昵称和描述
        MultiMatchQueryBuilder multiMatchQueryBuilder =
                QueryBuilders.multiMatchQuery(keyword,"title","nick","description");
        searchSourceBuilder.query(multiMatchQueryBuilder);
        // 将搜索源构建器设置到搜索请求中
        searchRequest.source(searchSourceBuilder);
        // 设置超时时间
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        // 高亮显示
        String[] array = {"title","nick","description"};
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        for (String key : array){
            highlightBuilder.fields().add(new HighlightBuilder.Field(key));
        }
        // 如果要显示多个字段，则设置requireFieldMatch为false
        highlightBuilder.requireFieldMatch(false);
        // 设置高亮标签
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        // 将高亮构建器设置到搜索源构建器中
        searchSourceBuilder.highlighter(highlightBuilder);
        //执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //处理结果
        List<Map<String, Object>> list = new ArrayList<>();
        // 遍历搜索结果中的每一项
        for (SearchHit hit:searchResponse.getHits()){
            // 处理高亮字段
            Map<String, HighlightField> highlightBuilderFields= hit.getHighlightFields();
            // 获取该搜索结果项的源数据
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            // 遍历需要处理的高亮字段名称数组
            for (String key : array){
                // 根据字段名获取对应的高亮字段对象
                HighlightField field = highlightBuilderFields.get(key);
                // 如果该字段存在高亮片段
                if (field != null){
                    // 获取所有的高亮片段
                    Text[] fragments = field.fragments();
                    // 将高亮片段数组转换为字符串，去除首尾的括号
                    String str = Arrays.toString(fragments);
                    str = str.substring(1,str.length()-1);
                    // 将处理后的高亮字符串存回源数据 map 中
                    sourceMap.put(key,str);
                }
            }
            // 将处理后的源数据 map 添加到结果列表中
            list.add(sourceMap);
        }
        return list;
    }

    @Override
    public void updateVideoViewCount(Long videoId) {
        Optional<Video> videoOpt = videoRepository.findById(videoId);
        if(videoOpt.isPresent()){
            Video video = videoOpt.get();
            int viewCount = video.getViewCount() == null? 0 : video.getViewCount();
            video.setViewCount(viewCount+1);
            videoRepository.save(video);
        }
    }


}
